#pragma once

#ifndef _CR_HTTP_TYPES_H_
#define _CR_HTTP_TYPES_H_

NS_CR_BEGIN

namespace http
{
	struct CaseInsensitiveCompare
	{
		bool operator() (const string& a, const string& b) const NOEXCEPT
		{
			return _STD lexicographical_compare(a.begin(), a.end(), b.begin(), b.end(),
				[](unsigned char ac, unsigned char bc) 
			{ 
				return _STD tolower(ac) < _STD tolower(bc); 
			});
		}
	};

	template<typename _Elem>
	class BodyImpl : public base_string<_Elem, _STD char_traits<_Elem>, _STD allocator<_Elem>, vl_char_traits<_Elem>>
	{
		using _MyBase = base_string<_Elem, _STD char_traits<_Elem>, _STD allocator<_Elem>, vl_char_traits<_Elem>>;
	public:
		BodyImpl()
			: _MyBase()
		{}

		BodyImpl(const _Elem* _el)
			: _MyBase(_el)
		{}

		BodyImpl(const _MyBase& _Right)
			: _MyBase(_Right)
		{}
	};


	using Header = _STD map<string, string, CaseInsensitiveCompare>;
	using Url  = string;
	using Body = BodyImpl<char>;

	class Uri
	{
	private:
		string _protocol;// http vs https
		string _domain;  // mail.google.com
		uint16_t _port;  // 80,443
		string _path;    // /mail/
		string _query;   // [after ?] a=b&c=b

	public:
		const string &protocol;
		const string &domain;
		const uint16_t &port;
		const string &path;
		const string &query;

		Uri(const string& url)
			: protocol(_protocol)
			, domain(_domain)
			, port(_port)
			, path(_path)
			, query(_query)
		{
			string u = url;
			u.trim_right("");
			size_t offset = 0, slash_pos, hash_pos, colon_pos, qmark_pos;
			string urlpath, urldomain, urlport;
			uint16_t default_port;

			static const char* allowed[] = { "https://", "http://", "ftp://", NULL };
			for (int i = 0; allowed[i] != NULL && this->_protocol.length() == 0; i++)
			{
				const char* c = allowed[i];
				if (u.compare(0, strlen(c), c) == 0) {
					offset = strlen(c);
					this->_protocol = string(c, 0, offset - 3);
				}
			}

			default_port = this->_protocol == "https" ? 443 : 80;
			slash_pos = u.find_first_of('/', offset + 1);
			urlpath = slash_pos == string::npos ? "/" : u.substr(slash_pos);
			urldomain = string(u.begin() + offset, slash_pos != string::npos ? u.begin() + slash_pos : u.end());
			urlpath = (hash_pos = urlpath.find("#")) != string::npos ? urlpath.substr(0, hash_pos) : urlpath;
			urlport = (colon_pos = urldomain.find(":")) != string::npos ? urldomain.substr(colon_pos + 1) : "";
			urldomain = urldomain.substr(0, colon_pos != string::npos ? colon_pos : urldomain.length());
			this->_domain = urldomain.to_lower();
			this->_query = (qmark_pos = urlpath.find("?")) != string::npos ? urlpath.substr(qmark_pos + 1) : "";
			this->_path = qmark_pos != string::npos ? urlpath.substr(0, qmark_pos) : urlpath;
			this->_port = urlport.length() == 0 ? default_port : urlport.to_number<int>();
		}
	};
}

NS_CR_END

#endif